﻿
/* /////////////////////////////////////////////////////////////////////////////////////////////////
https://www.patreon.com/user?u=33893211

Licensed under CC BY-SA after EarlyAccess ended. (see https://creativecommons.org/licenses/by-sa/4.0/)

///////////////////////////////////////////////////////////////////////////////////////////////// */

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

namespace PluginIdea
{
    public class ViewControl : MVRScript
    {
        private JSONStorableBool jsonStorableIsWorldSpace;
        private JSONStorableBool jsonStorableIsUpdateFocusPoint;
        private JSONStorableBool jsonStorableIsShowSpaceInfo;
        private JSONStorableFloat jsonStroableCamRange;

        private Transform centerTarget;
        private Transform camtarget;
        private Transform centerCameraTarget;

        private bool isCurrentTargetLocked;

        private FreeControllerV3 focusController;

        private Canvas popupCanvas;
        private Text popupText;

        private string currentSpaceStr;
        private string currentDirction = "None";

        private bool isInited;
        private RectTransform rt;


        public override void Init()
        {
            base.Init();

            if (SuperController.singleton.isOVR || SuperController.singleton.isOpenVR) return;

            if (containingAtom.type != "SessionPluginManager")
            {
                SuperController.LogError("need add on SessionPluginManager!");
                return;
            }

            jsonStorableIsWorldSpace = Utils.SetupToggle(this, "IsWorldSpace", true, false);
            jsonStorableIsShowSpaceInfo = Utils.SetupToggle(this, "IsShowSpaceInfo", true, false);
            jsonStroableCamRange = Utils.SetupSliderFloat(this, "FocusDistance", 1.5f, 0.5f, 10f, false);

            string helpStr = "View Align 1.0 \n\nIsWorldSpace: whether to align the world axis (otherwise to the local axis of its controller).\n\n" +
                "IsShowSpaceInfo: indicates whether to display the current coordinate system and the aligned direction (upper right corner).\n\n" +
                "FocusDistance: current focusing distance.\n\n" +
                "Shortcut keys (numeric keypad):\n\n" +
                "5: turn on/off target lock\n\n" +
                "4: Left view(axis x) \n\n" +
                "6: Right view(axis -x)\n\n" +
                "8: Back view(axis z)\n\n" +
                "2: Front view(axis -z)\n\n" +
                "1: Top view(axis -y)\n\n" +
                "3: Bottom view(axis y)\n\n" +
                "0: Switch between the world space and the local space";
            Utils.SetupInfoText(this, helpStr, 800, true);

            InitHUD();

            currentSpaceStr = jsonStorableIsWorldSpace.val ? "World" : "Local";
            jsonStorableIsWorldSpace.setCallbackFunction = (value) => {
                currentSpaceStr = value ? "World" : "Local";
            };

            jsonStorableIsShowSpaceInfo.setCallbackFunction = (value) => {
                popupText.enabled = value;
            };

            isInited = true;

            //keybinding
            SuperController.singleton.BroadcastMessage("OnActionsProviderAvailable", this, SendMessageOptions.DontRequireReceiver);

        }

        private void Update()
        {

            if (!isInited) return;

            if (Input.GetKeyDown(KeyCode.Keypad0))
            {
                jsonStorableIsWorldSpace.val = !jsonStorableIsWorldSpace.val;
                SyncStateInfo();
            }

            if (Input.GetKeyDown(KeyCode.Keypad5))
            {
                if (!isCurrentTargetLocked && focusController != null)
                {
                    isCurrentTargetLocked = true;
                }
                else
                {
                    isCurrentTargetLocked = false;
                }
                SyncStateInfo();
            }


            camtarget = SuperController.singleton.MonitorCenterCamera.transform;

            if (!isCurrentTargetLocked)
            {
                focusController = SuperController.singleton.GetSelectedController();
            }

            if (focusController != null)
            {
                centerTarget = focusController.focusPoint ?? focusController.transform;

                if (camtarget != null && centerTarget != null)
                {
                    if (jsonStorableIsWorldSpace.val)
                    {
                        if (Input.GetKeyDown(KeyCode.Keypad4))
                        {
                            LeftAlignInWorldSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad6))
                        {
                            RightAlignInWorldSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad8))
                        {
                            BackAlignInWorldSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad2))
                        {
                            ForwardAlignInWorldSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad1))
                        {
                            UpAlignInWorldSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad3))
                        {
                            BelowAlignInWorldSpace();
                        }
                    }
                    else
                    {
                        if (Input.GetKeyDown(KeyCode.Keypad4))
                        {
                            LeftAlignInLocalSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad6))
                        {
                            RightAlignInLocalSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad8))
                        {
                            BackAlignInLocalSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad2))
                        {
                            ForwardAlignInLocalSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad1))
                        {
                            UpAlignInLocalSpace();
                        }
                        else if (Input.GetKeyDown(KeyCode.Keypad3))
                        {
                            BelowAlignInLocalSpace();
                        }
                    }
                }
            }
        }

        private void BelowAlignInLocalSpace()
        {
            LookAtSelectedTarget(centerTarget.position - centerTarget.up * jsonStroableCamRange.val);
            currentDirction = "Down";
            SyncStateInfo();
        }

        private void UpAlignInLocalSpace()
        {
            LookAtSelectedTarget(centerTarget.position + centerTarget.up * jsonStroableCamRange.val);
            currentDirction = "Up";
            SyncStateInfo();
        }

        private void ForwardAlignInLocalSpace()
        {
            LookAtSelectedTarget(centerTarget.position + centerTarget.forward * jsonStroableCamRange.val);
            currentDirction = "Forward";
            SyncStateInfo();
        }

        private void BackAlignInLocalSpace()
        {
            LookAtSelectedTarget(centerTarget.position - centerTarget.forward * jsonStroableCamRange.val);
            currentDirction = "Back";
            SyncStateInfo();
        }

        private void RightAlignInLocalSpace()
        {
            LookAtSelectedTarget(centerTarget.position + centerTarget.right * jsonStroableCamRange.val);
            currentDirction = "Right";
            SyncStateInfo();
        }

        private void LeftAlignInLocalSpace()
        {
            LookAtSelectedTarget(centerTarget.position - centerTarget.right * jsonStroableCamRange.val);
            currentDirction = "Left";
            SyncStateInfo();
        }

        private void BelowAlignInWorldSpace()
        {
            LookAtSelectedTarget(centerTarget.position + Vector3.down * jsonStroableCamRange.val);
            currentDirction = "Down";
            SyncStateInfo();
        }

        private void UpAlignInWorldSpace()
        {
            LookAtSelectedTarget(centerTarget.position + Vector3.up * jsonStroableCamRange.val);
            currentDirction = "Up";
            SyncStateInfo();
        }

        private void ForwardAlignInWorldSpace()
        {
            LookAtSelectedTarget(centerTarget.position + Vector3.forward * jsonStroableCamRange.val);
            currentDirction = "Forward";
            SyncStateInfo();
        }

        private void BackAlignInWorldSpace()
        {
            LookAtSelectedTarget(centerTarget.position + Vector3.back * jsonStroableCamRange.val);
            currentDirction = "Back";
            SyncStateInfo();
        }

        private void RightAlignInWorldSpace()
        {
            LookAtSelectedTarget(centerTarget.position + Vector3.right * jsonStroableCamRange.val);
            currentDirction = "Right";
            SyncStateInfo();
        }

        private void LeftAlignInWorldSpace()
        {
            LookAtSelectedTarget(centerTarget.position + Vector3.left * jsonStroableCamRange.val);
            currentDirction = "Left";
            SyncStateInfo();
        }

        public void OnBindingsListRequested(List<object> bindings)
        {
            bindings.Add(jsonStorableIsWorldSpace);

            bindings.Add(new JSONStorableAction("Local Left", LeftAlignInLocalSpace));
            bindings.Add(new JSONStorableAction("Local Right", RightAlignInLocalSpace));
            bindings.Add(new JSONStorableAction("Local Forward", ForwardAlignInLocalSpace));
            bindings.Add(new JSONStorableAction("Local Back", BackAlignInLocalSpace));
            bindings.Add(new JSONStorableAction("Local Up", UpAlignInLocalSpace));
            bindings.Add(new JSONStorableAction("Local Down", BelowAlignInLocalSpace));

            bindings.Add(new JSONStorableAction("World Left", LeftAlignInWorldSpace));
            bindings.Add(new JSONStorableAction("World Right", RightAlignInWorldSpace));
            bindings.Add(new JSONStorableAction("World Forward", ForwardAlignInWorldSpace));
            bindings.Add(new JSONStorableAction("World Back", BackAlignInWorldSpace));
            bindings.Add(new JSONStorableAction("World Up", UpAlignInWorldSpace));
            bindings.Add(new JSONStorableAction("World Down", BelowAlignInWorldSpace));
        }

        private void SyncStateInfo()
        {
            string lockStr = isCurrentTargetLocked ? string.Format("Current Locked: {0}", focusController.name) : "";
            popupText.text = string.Format("{0}-{1} \n\n {2}", currentSpaceStr, currentDirction, lockStr);
        }

        private void InitHUD()
        {
            popupCanvas = Utils.AppendCanvasObjectUnderHeadCenter("ViewAlignSpaceCanvas", false);
            var canvasObj = popupCanvas.gameObject;

            GameObject textObj = new GameObject();
            textObj.name = "SpaceInfo";
            textObj.transform.parent = canvasObj.transform;
            textObj.transform.localScale = Vector3.one;
            textObj.transform.localPosition = new Vector3(400, 250, 0);
            textObj.transform.localRotation = Quaternion.identity;
            popupText = textObj.AddComponent<Text>();
            RectTransform rt2 = textObj.GetComponent<RectTransform>();
            rt2.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 500);
            rt2.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 100);
            popupText.alignment = TextAnchor.MiddleCenter;
            popupText.horizontalOverflow = HorizontalWrapMode.Overflow;
            popupText.verticalOverflow = VerticalWrapMode.Overflow;
            Font ArialFont = (Font)Resources.GetBuiltinResource(typeof(Font), "Arial.ttf");
            popupText.font = ArialFont;
            popupText.fontSize = 20;
            popupText.text = "Perpective";
            popupText.enabled = true;
            popupText.color = Color.white;
        }

        private void LookAtSelectedTarget(Vector3 monitorCamTargetPos)
        {
            Transform navRigTrans = SuperController.singleton.navigationRig;
            camtarget = SuperController.singleton.MonitorCenterCamera.transform;

            Vector3 offset = monitorCamTargetPos - camtarget.position;

            if (offset.magnitude > 0.2f && Vector3.Angle(offset.normalized, camtarget.forward) < 0.1f)
            {
                navRigTrans.position = navRigTrans.position + offset + new Vector3(0.01f, 0.01f, 0.01f);
            }
            else
            {
                navRigTrans.position = navRigTrans.position + offset;
            }

            SuperController.singleton.FocusOnController(focusController);
        }

        private void OnDestroy()
        {
            if (popupCanvas != null)
            {
                Destroy(popupCanvas.gameObject);
            }

            SuperController.singleton.BroadcastMessage("OnActionsProviderDestroyed", this, SendMessageOptions.DontRequireReceiver);
        }
    }

}


